home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue53 / Alfresco / AASimAnn.pas next >
Encoding:
Pascal/Delphi Source File  |  1999-11-27  |  22.7 KB  |  738 lines

  1. {*********************************************************}
  2. {* AASimAnn                                              *}
  3. {* Copyright (c) Julian M Bucknall 1998-1999             *}
  4. {* All rights reserved.                                  *}
  5. {*********************************************************}
  6. {* Algorithms Alfresco simulated annealing unit          *}
  7. {*********************************************************}
  8.  
  9. {Note: this unit is released as freeware. In other words, you are free
  10.        to use this unit in your own applications, however I retain all
  11.        copyright to the code. JMB}
  12.  
  13. unit AASimAnn;
  14.  
  15. interface
  16.  
  17. uses
  18.   SysUtils,
  19.   Classes;
  20.  
  21. {===Classes for the traveling salesman problem===}
  22. type
  23.   TaaCity = class {a city object}
  24.     private
  25.       FX : double;
  26.       FY : double;
  27.     protected
  28.     public
  29.       constructor Create(aX, aY : double);
  30.         {-create a city with given coordinates}
  31.       constructor CreateRandom;
  32.         {-create a city with random coordinates in the range [0..100)}
  33.  
  34.       function Distance(aCity : TaaCity) : double;
  35.         {-calculate the distance to another city}
  36.  
  37.       property X : double read FX;
  38.         {-X coordinate}
  39.       property Y : double read FY;
  40.         {-Y coordinate}
  41.   end;
  42.  
  43.   TaaTour = class {a tour of cities}
  44.     private
  45.       FList : TList;
  46.       FDistance : double;
  47.     protected
  48.       function GetCity(aIndex : integer) : TaaCity;
  49.       function GetCount : integer;
  50.       function GetDistance : double;
  51.  
  52.       procedure Reverse(aStartInx, aEndInx : integer);
  53.     public
  54.       constructor Create;
  55.         {-create a tour}
  56.       destructor Destroy; override;
  57.         {-destroy the tour}
  58.  
  59.       procedure AddCity(aCity : TaaCity);
  60.         {-append a city to the tour}
  61.       procedure Assign(aTour : TaaTour);
  62.         {-copy the given tour to ours}
  63.       procedure LoadFromFile(aName : string);
  64.         {-load a tour from a file}
  65.  
  66.       function GetPossibleChange(var aUseRelocate : boolean;
  67.                                  var aStartInx    : integer;
  68.                                  var aEndInx      : integer;
  69.                                  var aToInx       : integer) : double;
  70.         {-generate a possible change and its difference in distance;
  71.           aUseRelocate will be true if the change requires a
  72.           relocation of a range of cities, false if the range is to be
  73.           reversed}
  74.       procedure ApplyChange(aUseRelocate : boolean;
  75.                             aStartInx    : integer;
  76.                             aEndInx      : integer;
  77.                             aToInx       : integer;
  78.                             aDeltaDist   : double);
  79.         {-apply a change calculated by GetPossibleChange}
  80.  
  81.       property City[aIndex : integer] : TaaCity
  82.                   read GetCity; default;
  83.         {-the cities in the tour as an array}
  84.       property Count : integer read GetCount;
  85.         {-the number of cities in the tour}
  86.       property Distance : double read GetDistance;
  87.         {-the total distance of the tour}
  88.   end;
  89.  
  90.  
  91. {===Classes for the knapsack problem===}
  92. type
  93.   TaaArticle = class {an article with value and size}
  94.     private
  95.       FValue : double;
  96.       FSize  : double;
  97.     protected
  98.     public
  99.       constructor Create(aValue, aSize : double);
  100.         {-create an article with given attributes}
  101.       constructor CreateRandom;
  102.         {-create an article with random attributes}
  103.  
  104.       property Value : double read FValue;
  105.         {-value}
  106.       property Size : double read FSize;
  107.         {-size}
  108.   end;
  109.  
  110.   TaaKnapsack = class {a knapsack of articles}
  111.     private
  112.       FList     : TList;
  113.       FValue    : double;
  114.       FFitCount : integer;
  115.       FFitSize  : double;
  116.       FSize     : double; 
  117.     protected
  118.       function GetArticle(aIndex : integer) : TaaArticle;
  119.       function GetCount : integer;
  120.       function GetValue : double;
  121.     public
  122.       constructor Create(aSize : double);
  123.         {-create a knapsack of a certain size}
  124.       destructor Destroy; override;
  125.         {-destroy the knapsack}
  126.  
  127.       procedure AddArticle(aArticle : TaaArticle);
  128.         {-append a possible article to the knapsack}
  129.       procedure Assign(aKnapsack : TaaKnapsack);
  130.         {-copy the given knapsack to ours}
  131.       procedure LoadFromFile(aName : string);
  132.         {-load a knapsack from a file}
  133.  
  134.       procedure GenerateChange;
  135.         {-randomly generate a possible change}
  136.  
  137.       property Article[aIndex : integer] : TaaArticle
  138.                   read GetArticle; default;
  139.         {-the articles in the knapsack as an array}
  140.       property Count : integer read GetCount;
  141.         {-the number of possible articles in the knapsack}
  142.       property Value : double read GetValue;
  143.         {-the total value of the articles that fit in the knapsack}
  144.       property FitCount : integer read FFitCount;
  145.         {-the number of articles that fit in the knapsack}
  146.       property FitSize : double read FFitSize;
  147.         {-the total size of articles that fit in the knapsack}
  148.       property Size : double read FSize;
  149.         {-the actual size of the knapsack}
  150.   end;
  151.  
  152.  
  153.  
  154. procedure TravelingSalesman(aCityCount : integer;
  155.                             aLog       : TStream);
  156.   {-solve a travelling salesman problem with aCityCount cities,
  157.     randomly placed, writing the details to the stream aLog}
  158.  
  159. procedure SolveKnapsackProblem(aArticleCount : integer;
  160.                                aLog          : TStream);
  161.   {-solve a knapsack problem with aArticleCount articles,
  162.     randomly generated, writing the details to the stream aLog}
  163.  
  164. implementation
  165.  
  166. {===TaaCity==========================================================}
  167. constructor TaaCity.Create(aX, aY : double);
  168. begin
  169.   inherited Create;
  170.   FX := aX;
  171.   FY := aY;
  172. end;
  173. {--------}
  174. constructor TaaCity.CreateRandom;
  175. begin
  176.   inherited Create;
  177.   FX := Random * 100;
  178.   FY := Random * 100;
  179. end;
  180. {--------}
  181. function TaaCity.Distance(aCity : TaaCity) : double;
  182. begin
  183.   Result := Sqrt(Sqr(X - aCity.X) + Sqr(Y - aCity.Y));
  184. end;
  185. {====================================================================}
  186.  
  187.  
  188. {===TaaTour==========================================================}
  189. constructor TaaTour.Create;
  190. begin
  191.   inherited Create;
  192.   FList := TList.Create;
  193. end;
  194. {--------}
  195. destructor TaaTour.Destroy;
  196. begin
  197.   FList.Free;
  198.   inherited Destroy;
  199. end;
  200. {--------}
  201. procedure TaaTour.AddCity(aCity : TaaCity);
  202. begin
  203.   FList.Add(aCity);
  204. end;
  205. {--------}
  206. procedure TaaTour.ApplyChange(aUseRelocate : boolean;
  207.                               aStartInx    : integer;
  208.                               aEndInx      : integer;
  209.                               aToInx       : integer;
  210.                               aDeltaDist   : double);
  211. begin
  212.   if aUseRelocate then begin
  213.     if (aToInx < aStartInx) then begin
  214.       Reverse(aToInx, aStartInx-1);
  215.       Reverse(aStartInx, aEndInx);
  216.       Reverse(aToInx, aEndInx);
  217.     end
  218.     else begin
  219.       Reverse(aStartInx, aEndInx);
  220.       Reverse(aEndInx+1, aToInx-1);
  221.       Reverse(aStartInx, aToInx-1);
  222.     end;
  223.   end
  224.   else begin
  225.     Reverse(aStartInx, aEndInx);
  226.   end;
  227.   FDistance := Distance + aDeltaDist;
  228. end;
  229. {--------}
  230. procedure TaaTour.Assign(aTour : TaaTour);
  231. var
  232.   i : integer;
  233. begin
  234.   if (FList.Count = aTour.FList.Count) then begin
  235.     for i := 0 to pred(aTour.Count) do
  236.       FList[i] := aTour.FList[i];
  237.   end
  238.   else begin
  239.     FList.Clear;
  240.     for i := 0 to pred(aTour.Count) do begin
  241.       FList.Add(aTour[i]);
  242.     end;
  243.   end;
  244.   FDistance := aTour.Distance;
  245. end;
  246. {--------}
  247. function TaaTour.GetCity(aIndex : integer) : TaaCity;
  248. begin
  249.   Result := FList[aIndex];
  250. end;
  251. {--------}
  252. function TaaTour.GetCount : integer;
  253. begin
  254.   Result := FList.Count;
  255. end;
  256. {--------}
  257. function TaaTour.GetDistance : double;
  258. var
  259.   i : integer;
  260. begin
  261.   if (FDistance <= 0.0) then begin
  262.     FDistance := 0.0;
  263.     for i := 0 to (FList.Count - 2) do
  264.       FDistance := FDistance + City[i].Distance(City[i+1]);
  265.     FDistance := FDistance + City[0].Distance(City[pred(FList.Count)]);
  266.   end;
  267.   Result := FDistance;
  268. end;
  269. {--------}
  270. function TaaTour.GetPossibleChange(var aUseRelocate : boolean;
  271.                                    var aStartInx    : integer;
  272.                                    var aEndInx      : integer;
  273.                                    var aToInx       : integer) : double;
  274. var
  275.   Temp : integer;
  276.   Inx1 : integer;
  277.   Inx2 : integer;
  278.   Inx3 : integer;
  279. begin
  280.   {first determine the type of change}
  281.   aUseRelocate := Random < 0.5;
  282.   {for a relocation...}
  283.   if aUseRelocate then begin
  284.     {generate three random indexes greater than 0}
  285.     Inx1 := Random(pred(Count))+1;
  286.     repeat
  287.       Inx2 := Random(pred(Count))+1;
  288.     until (Inx2 <> Inx1);
  289.     repeat
  290.       Inx3 := Random(pred(Count))+1;
  291.     until (Inx3 <> Inx1) and (Inx3 <> Inx2);
  292.     {sort them}
  293.     if (Inx1 > Inx2) then begin
  294.       Temp := Inx1; Inx1 := Inx2; Inx2 := Temp;
  295.     end;
  296.     if (Inx1 > Inx3) then begin
  297.       Temp := Inx1; Inx1 := Inx3; Inx3 := Temp;
  298.     end;
  299.     if (Inx2 > Inx3) then begin
  300.       Temp := Inx2; Inx2 := Inx3; Inx3 := Temp;
  301.     end;
  302.     {half the time make the insert point the first index, the other
  303.      half make it the last one; calculate the difference in distance}
  304.     Result := 0.0;
  305.     if (Random < 0.5) then begin
  306.       aToInx := Inx1;
  307.       aStartInx := Inx2;
  308.       aEndInx := Inx3;
  309.       Result := Result - City[Inx1-1].Distance(City[Inx1])
  310.                        + City[Inx1-1].Distance(City[Inx2])
  311.                        - City[Inx2-1].Distance(City[Inx2])
  312.                        + City[Inx3].Distance(City[Inx1]);
  313.       if (Inx3 < pred(Count)) then
  314.         Result := Result - City[Inx3].Distance(City[Inx3+1])
  315.                          + City[Inx2-1].Distance(City[Inx3+1])
  316.       else
  317.         Result := Result - City[Inx3].Distance(City[0])
  318.                          + City[Inx2-1].Distance(City[0])
  319.     end
  320.     else begin
  321.       aStartInx := Inx1;
  322.       aEndInx := Inx2;
  323.       aToInx := Inx3;
  324.       if (Inx3 = Inx2+1) then {there's no change}
  325.         Exit;
  326.       Result := Result - City[Inx1-1].Distance(City[Inx1])
  327.                        - City[Inx2].Distance(City[Inx2+1])
  328.                        - City[Inx3-1].Distance(City[Inx3]);
  329.       Result := Result + City[Inx1-1].Distance(City[Inx2+1])
  330.                        + City[Inx3-1].Distance(City[Inx1])
  331.                        + City[Inx2].Distance(City[Inx3]);
  332.     end;
  333.   end
  334.   {for a reversal...}
  335.   else begin
  336.     {generate two random indexes}
  337.     Inx1 := Random(pred(Count))+1;
  338.     repeat
  339.       Inx2 := Random(pred(Count))+1;
  340.     until (Inx2 <> Inx1);
  341.     {sort them}
  342.     if (Inx1 > Inx2) then begin
  343.       Temp := Inx1; Inx1 := Inx2; Inx2 := Temp;
  344.     end;
  345.     {return the indexes}
  346.     aStartInx := Inx1;
  347.     aEndInx := Inx2;
  348.     {now calculate the difference in distance}
  349.     Result := 0.0;
  350.     Result := Result - City[Inx1-1].Distance(City[Inx1])
  351.                      + City[Inx1-1].Distance(City[Inx2]);
  352.     if (Inx2 < pred(Count)) then
  353.       Result := Result - City[Inx2].Distance(City[Inx2+1])
  354.                        + City[Inx1].Distance(City[Inx2+1])
  355.     else
  356.       Result := Result - City[Inx2].Distance(City[0])
  357.                        + City[Inx1].Distance(City[0])
  358.   end;
  359. end;
  360. {--------}
  361. procedure TaaTour.LoadFromFile(aName : string);
  362. var
  363.   F : text;
  364.   X, Y : double;
  365. begin
  366.   System.Assign(F, aName);
  367.   System.Reset(F);
  368.   repeat
  369.     readln(F, X, Y);
  370.     FList.Add(TaaCity.Create(X, Y));
  371.   until EOF(F);
  372.   System.Close(F);
  373. end;
  374. {--------}
  375. procedure TaaTour.Reverse(aStartInx, aEndInx : integer);
  376. begin
  377.   while (aStartInx < aEndInx) do begin
  378.     FList.Exchange(aStartInx, aEndInx);
  379.     inc(aStartInx);
  380.     dec(aEndInx);
  381.   end;
  382. end;
  383. {====================================================================}
  384.  
  385. {===TaaArticle=======================================================}
  386. constructor TaaArticle.Create(aValue, aSize : double);
  387. begin
  388.   inherited Create;
  389.   FValue := aValue;
  390.   FSize := aSize;
  391. end;
  392. {--------}
  393. constructor TaaArticle.CreateRandom;
  394. begin
  395.   inherited Create;
  396.   FValue := Random * 100;
  397.   FSize := Random * 100;
  398. end;
  399. {====================================================================}
  400.  
  401.  
  402. {===TaaKnapsack======================================================}
  403. constructor TaaKnapsack.Create(aSize : double);
  404. begin
  405.   inherited Create;
  406.   FList := TList.Create;
  407.   FSize := aSize;
  408. end;
  409. {--------}
  410. destructor TaaKnapsack.Destroy;
  411. begin
  412.   FList.Free;
  413.   inherited Destroy;
  414. end;
  415. {--------}
  416. procedure TaaKnapsack.AddArticle(aArticle : TaaArticle);
  417. begin
  418.   FList.Add(aArticle);
  419. end;
  420. {--------}
  421. procedure TaaKnapsack.Assign(aKnapsack : TaaKnapsack);
  422. var
  423.   i : integer;
  424. begin
  425.   if (FList.Count = aKnapsack.FList.Count) then begin
  426.     for i := 0 to pred(FList.Count) do
  427.       FList[i] := aKnapsack.FList[i];
  428.   end
  429.   else begin
  430.     FList.Clear;
  431.     for i := 0 to pred(aKnapsack.Count) do
  432.       FList.Add(aKnapsack[i]);
  433.   end;
  434.   FValue := aKnapsack.Value;
  435.   {the above line will ensure that FFitCount and FFitSize are set
  436.    properly}
  437.   FFitCount := aKnapsack.FFitCount;
  438.   FFitSize := aKnapsack.FFitSize;
  439. end;
  440. {--------}
  441. function TaaKnapsack.GetArticle(aIndex : integer) : TaaArticle;
  442. begin
  443.   Result := FList[aIndex];
  444. end;
  445. {--------}
  446. function TaaKnapsack.GetCount : integer;
  447. begin
  448.   Result := FList.Count;
  449. end;
  450. {--------}
  451. function TaaKnapsack.GetValue : double;
  452. var
  453.   i        : integer;
  454.   WorkSize : double;
  455.   AllFit   : boolean;
  456. begin
  457.   if (FValue <= 0.0) then begin
  458.     FValue := 0.0;
  459.     WorkSize := 0.0;
  460.     AllFit := true;
  461.     for i := 0 to pred(FList.Count) do begin
  462.       WorkSize := WorkSize + Article[i].Size;
  463.       if (WorkSize <= Size) then
  464.         FValue := FValue + Article[i].Value
  465.       else {the last article didn't fit} begin
  466.         AllFit := false;
  467.         FFitSize := WorkSize - Article[i].Size;
  468.         FFitCount := i;
  469.         Break;
  470.       end;
  471.     end;
  472.     if AllFit then begin
  473.       FFitCount := FList.Count;
  474.       FFitSize := WorkSize;
  475.     end;
  476.   end;
  477.   Result := FValue;
  478. end;
  479. {--------}
  480. procedure TaaKnapsack.GenerateChange;
  481. var
  482.   Inx1 : integer;
  483.   Inx2 : integer;
  484. begin
  485.   {what we shall do is to swap a random article that fits in the
  486.    knapsack with one that isn't there yet}
  487.   Inx1 := Random(FitCount);
  488.   Inx2 := Random(Count - FitCount) + FitCount;
  489.   FList.Exchange(Inx1, Inx2);
  490.   {ensure the value of the kanpsack gets recalculated at the earliest
  491.    opportunity}
  492.   FValue := 0.0;
  493. end;
  494. {--------}
  495. procedure TaaKnapsack.LoadFromFile(aName : string);
  496. var
  497.   F : text;
  498.   Value, Size : double;
  499. begin
  500.   System.Assign(F, aName);
  501.   System.Reset(F);
  502.   repeat
  503.     readln(F, Value, Size);
  504.     FList.Add(TaaArticle.Create(Value, Size));
  505.   until EOF(F);
  506.   System.Close(F);
  507. end;
  508. {====================================================================}
  509.  
  510.  
  511. {===Helper routines==================================================}
  512. procedure PrintKnapsack(const aMsg      : string;
  513.                               aKnapsack : TaaKnapsack;
  514.                               aLog      : TStream);
  515. var
  516.   i         : integer;
  517.   LogString : string;
  518. begin
  519.   aLog.Write(aMsg[1], length(aMsg));
  520.   LogString := ^M^J'   Value      Size'^M^J;
  521.   aLog.Write(LogString[1], length(LogString));
  522.   for i := 0 to pred(aKnapsack.Count) do begin
  523.     if (i = aKnapsack.FitCount) then begin
  524.       LogString := Format('--knapsack full-- (total size: %9.6f)'^M^J,
  525.                           [aKnapsack.FitSize]);
  526.       aLog.Write(LogString[1], length(LogString));
  527.     end;
  528.     LogString := Format('(%9.6f, %9.6f)'^M^J,
  529.                         [aKnapsack.Article[i].Value,
  530.                          aKnapsack.Article[i].Size]);
  531.     aLog.Write(LogString[1], length(LogString));
  532.   end;
  533. end;
  534. {--------}
  535. procedure PrintTour(const aMsg  : string;
  536.                           aTour : TaaTour;
  537.                           aLog  : TStream);
  538. var
  539.   i         : integer;
  540.   LogString : string;
  541. begin
  542.   aLog.Write(aMsg[1], length(aMsg));
  543.   LogString := ^M^J;
  544.   aLog.Write(LogString[1], length(LogString));
  545.   for i := 0 to pred(aTour.Count) do begin
  546.     LogString := Format('(%9.6f, %9.6f)'^M^J,
  547.                         [aTour.City[i].X, aTour.City[i].Y]);
  548.     aLog.Write(LogString[1], length(LogString));
  549.   end;
  550. end;
  551. {====================================================================}
  552.  
  553.  
  554. {===Interfaced Routines==============================================}
  555. procedure TravelingSalesman(aCityCount : integer;
  556.                             aLog       : TStream);
  557. var
  558.   i           : integer;
  559.   MainTour    : TaaTour;
  560.   BestTour    : TaaTour;
  561.   LoopCount   : integer;
  562.   Temp        : double;
  563.   DeltaEnergy : double;
  564.   LogString   : string;
  565.   LowerCount, HigherCount : integer;
  566.   UseRelocate : boolean;
  567.   StartInx    : integer;
  568.   EndInx      : integer;
  569.   ToInx       : integer;
  570.   StopCount   : integer;
  571. begin
  572.   StopCount := 10 * aCityCount;
  573.   MainTour := nil;
  574.   BestTour := nil;
  575.   try
  576.     {create the cities}
  577.     MainTour := TaaTour.Create;
  578.     (*
  579.     MainTour.LoadFromFile('file1.txt');
  580.     *)
  581.     for i := 1 to aCityCount do
  582.       MainTour.AddCity(TaaCity.CreateRandom);
  583.     {write out the cities to the log file}
  584.     PrintTour('Original city list', MainTour, aLog);
  585.     {save this as the best tour so far}
  586.     BestTour := TaaTour.Create;
  587.     BestTour.Assign(MainTour);
  588.     {set the temperature}
  589.     Temp := 1000.0;
  590.     LoopCount := 0;
  591.     LowerCount := 0;
  592.     HigherCount := 0;
  593.     {continue until we freeze}
  594.     LogString := ^M^J'Annealing beginning...'^M^J;
  595.     aLog.Write(LogString[1], length(LogString));
  596.     while (Temp > 0.002) do begin
  597.       {this is one more loop}
  598.       inc(LoopCount);
  599.       {select a possible shuffling, get the change in distance}
  600.       DeltaEnergy := MainTour.GetPossibleChange(UseRelocate,
  601.                                                 StartInx, EndInx,
  602.                                                 ToInx);
  603.       {if we did better, save this tour as the best one so far}
  604.       if (DeltaEnergy < 0.0) then begin
  605.         MainTour.ApplyChange(UseRelocate,
  606.                              StartInx, EndInx, ToInx, DeltaEnergy);
  607.         BestTour.Assign(MainTour);
  608.         inc(LowerCount);
  609.       end
  610.       {if we did worse, check Boltzmann's probability, and if our
  611.        'coin toss' is less than this, use this as the best tour so
  612.        far}
  613.       else if (DeltaEnergy > 0.0) then begin
  614.         if (Random < Exp(-DeltaEnergy/Temp)) then begin
  615.           MainTour.ApplyChange(UseRelocate,
  616.                                StartInx, EndInx, ToInx, DeltaEnergy);
  617.           BestTour.Assign(MainTour);
  618.           inc(HigherCount);
  619.         end;
  620.       end;
  621.       {if we've done the required number of loops at this temperature,
  622.        reduce the temperature by 1%}
  623.       if (LoopCount >= StopCount) then begin
  624.         LogString := Format('Temp=%.3f; TourDistance=%.3f; Lower=%d; Higher=%d'^M^J,
  625.                             [Temp, BestTour.Distance, LowerCount, HigherCount]);
  626.         aLog.Write(LogString[1], length(LogString));
  627.         Temp := Temp * 0.99;
  628.         LoopCount := 0;
  629.         LowerCount := 0;
  630.         HigherCount := 0;
  631.       end;
  632.     end;
  633.     LogString := ^M^J'Annealing complete'^M^J;
  634.     aLog.Write(LogString[1], length(LogString));
  635.     PrintTour('--best tour', BestTour, aLog);
  636.     LogString := Format('--best tour distance=%.3f'^M^J,
  637.                         [BestTour.Distance]);
  638.     aLog.Write(LogString[1], length(LogString));
  639.   finally
  640.     for i := 0 to pred(MainTour.Count) do
  641.       MainTour.City[i].Free;
  642.     MainTour.Free;
  643.     BestTour.Free;
  644.   end;
  645. end;
  646. {--------}
  647. procedure SolveKnapsackProblem(aArticleCount : integer;
  648.                                aLog          : TStream);
  649. var
  650.   i           : integer;
  651.   MainSack    : TaaKnapsack;
  652.   TestSack    : TaaKnapsack;
  653.   BestSack    : TaaKnapsack;
  654.   LoopCount   : integer;
  655.   Temp        : double;
  656.   DeltaEnergy : double;
  657.   LogString   : string;
  658.   LowerCount, HigherCount : integer;
  659.   StopCount   : integer;
  660. begin
  661.   StopCount := 10 * aArticleCount;
  662.   MainSack := nil;
  663.   BestSack := nil;
  664.   TestSack := nil;
  665.   try
  666.     {create a knapsack and the articles; set the size of the knapsack
  667.      so that about 1/10 of the articles will fit}
  668.     MainSack := TaaKnapsack.Create(aArticleCount * 5.0);
  669.     for i := 1 to aArticleCount do
  670.       MainSack.AddArticle(TaaArticle.CreateRandom);
  671.     {save this as the best packing so far}
  672.     BestSack := TaaKnapsack.Create(aArticleCount * 5.0);
  673.     BestSack.Assign(MainSack);
  674.     {create the test sack}
  675.     TestSack := TaaKnapsack.Create(aArticleCount * 5.0);
  676.     {write out the articles to the log file}
  677.     PrintKnapsack('Original article list', MainSack, aLog);
  678.     {set the temperature}
  679.     Temp := 500.0;
  680.     LoopCount := 0;
  681.     LowerCount := 0;
  682.     HigherCount := 0;
  683.     {continue until the temperature is just above freezing}
  684.     LogString := ^M^J'Annealing beginning...'^M^J;
  685.     aLog.Write(LogString[1], length(LogString));
  686.     while (Temp > 0.002) do begin
  687.       {this is one more loop}
  688.       inc(LoopCount);
  689.       {select a possible shuffling, get the change in value}
  690.       TestSack.Assign(MainSack);
  691.       TestSack.GenerateChange;
  692.       DeltaEnergy := MainSack.Value - TestSack.Value;
  693.       {if we did better, save this packing as the best one so far}
  694.       if (DeltaEnergy < 0.0) then begin
  695.         MainSack.Assign(TestSack);
  696.         BestSack.Assign(MainSack);
  697.         inc(LowerCount);
  698.       end
  699.       {if we did worse, check Boltzmann's probability, and if our
  700.        'coin toss' is less than this, use this as the best tour so
  701.        far}
  702.       else if (DeltaEnergy > 0.0) then begin
  703.         if (Random < Exp(-DeltaEnergy/Temp)) then begin
  704.           MainSack.Assign(TestSack);
  705.           BestSack.Assign(MainSack);
  706.           inc(HigherCount);
  707.         end;
  708.       end;
  709.       {if we've done the required number of loops at this temperature,
  710.        reduce the temperature by 1%}
  711.       if (LoopCount >= StopCount) then begin
  712.         LogString := Format('Temp=%.3f; Knapsack value=%.3f; Lower=%d; Higher=%d'^M^J,
  713.                             [Temp, BestSack.Value, LowerCount, HigherCount]);
  714.         aLog.Write(LogString[1], length(LogString));
  715.         Temp := Temp * 0.99;
  716.         LoopCount := 0;
  717.         LowerCount := 0;
  718.         HigherCount := 0;
  719.       end;
  720.     end;
  721.     LogString := ^M^J'Annealing complete'^M^J;
  722.     aLog.Write(LogString[1], length(LogString));
  723.     PrintKnapsack('--best packing', BestSack, aLog);
  724.     LogString := Format('--best packing value=%.3f'^M^J,
  725.                         [BestSack.Value]);
  726.     aLog.Write(LogString[1], length(LogString));
  727.   finally
  728.     for i := 0 to pred(MainSack.Count) do
  729.       MainSack.Article[i].Free;
  730.     MainSack.Free;
  731.     BestSack.Free;
  732.     TestSack.Free;
  733.   end;
  734. end;
  735. {====================================================================}
  736.  
  737. end.
  738.